﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataStuctureStudy.Graphs
{
    class GraphTest
    {
        public static void Test()
        {
            var graph = new Graph<String>(50);
            graph
                .AddVertex("A")
                .AddVertex("B")
                .AddVertex("C")
                .AddVertex("D")
                .AddVertex("E")
                .AddVertex("F")
                .AddVertex("G")
                .AddVertex("H")
                .AddVertex("I");
            graph
                .AddDirectedEdge("A", "B").AddDirectedEdge("A", "C").AddDirectedEdge("A", "D").AddDirectedEdge("A", "E")
                .AddDirectedEdge("B", "F")
                .AddDirectedEdge("D", "G")
                .AddDirectedEdge("F", "H")
                .AddDirectedEdge("G", "I");

            Console.WriteLine("深度遍历，栈版本：");
            graph.DepthFirstSearch("A", Console.Write);
            Console.WriteLine();
            Console.WriteLine();

            Console.WriteLine("深度遍历，递归版本：");
            graph.DepthFirstSearchRecursion("A", Console.Write);
            Console.WriteLine();
            Console.WriteLine();

            Console.WriteLine("广度遍历：");
            graph.BreadthFirstSearch("A", Console.Write);
            Console.WriteLine();
            Console.WriteLine();

            Console.WriteLine("最小生成树：");
            graph.DisplayMinimumSpanningTree("A");
            Console.WriteLine();
            Console.WriteLine();

            Console.WriteLine("拓扑排序：");
            var results = graph.TopologySort();
            Console.WriteLine(String.Join("->", results.ToArray()));
            Console.WriteLine();
        }

        class Vertex<T>
        {
            public T Value { get; set; }

            public bool WasVisited { get; set; }
        }

        class Graph<T>
        {
            #region 私有字段

            private int _maxSize;
            private Vertex<T>[] _vertexs;
            private bool[][] _edges;
            private int _vertexCount = 0;

            #endregion

            #region 构造方法

            public Graph(int maxSize)
            {
                _maxSize = maxSize;
                _vertexs = new Vertex<T>[_maxSize];
                _edges = new bool[_maxSize][];
                for (var i = 0; i < _maxSize; i++)
                {
                    _edges[i] = new bool[_maxSize];
                }
            }

            #endregion

            #region 添加顶点

            public Graph<T> AddVertex(T value)
            {
                _vertexs[_vertexCount++] = new Vertex<T> { Value = value };

                return this;
            }

            #endregion

            #region 添加边

            public Graph<T> AddUnDirectedEdge(T startItem, T endItem)
            {
                var startIndex = this.GetVertexIndex(startItem);
                var endIndex = this.GetVertexIndex(endItem);

                _edges[startIndex][endIndex] = true;
                _edges[endIndex][startIndex] = true;

                return this;
            }

            public Graph<T> AddDirectedEdge(T startItem, T endItem)
            {
                var startIndex = this.GetVertexIndex(startItem);
                var endIndex = this.GetVertexIndex(endItem);

                _edges[startIndex][endIndex] = true;

                return this;
            }

            #endregion

            #region 深度优先遍历：栈版本

            public void DepthFirstSearch(T startItem, Action<T> action)
            {
                var startIndex = this.GetVertexIndex(startItem);
                var stack = new Stack<int>();

                this.DepthFirstSearchVisit(stack, startIndex, action);
                while (stack.Count > 0)
                {
                    var adjoinVertexIndex = this.GetNextUnVisitedAdjoinVertexIndex(stack.Peek());
                    if (adjoinVertexIndex >= 0)
                    {
                        this.DepthFirstSearchVisit(stack, adjoinVertexIndex, action);
                    }
                    else
                    {
                        stack.Pop();
                    }
                }

                this.ResetVisited();
            }

            private void DepthFirstSearchVisit(Stack<int> stack, int index, Action<T> action)
            {
                _vertexs[index].WasVisited = true;
                action(_vertexs[index].Value);
                stack.Push(index);
            }

            #endregion

            #region 深度优先遍历：递归版本

            public void DepthFirstSearchRecursion(T startItem, Action<T> action)
            {
                var startIndex = this.GetVertexIndex(startItem);

                this.DepthFirstSearchRecursionVisit(startIndex, action);

                this.ResetVisited();
            }

            private void DepthFirstSearchRecursionVisit(int index, Action<T> action)
            {
                _vertexs[index].WasVisited = true;
                action(_vertexs[index].Value);

                var adjoinVertexIndex = this.GetNextUnVisitedAdjoinVertexIndex(index);
                while (adjoinVertexIndex >= 0)
                {
                    this.DepthFirstSearchRecursionVisit(adjoinVertexIndex, action);
                    adjoinVertexIndex = this.GetNextUnVisitedAdjoinVertexIndex(index);
                }
            }

            #endregion

            #region 广度优先遍历

            public void BreadthFirstSearch(T startItem, Action<T> action)
            {
                var startIndex = this.GetVertexIndex(startItem);
                var queue = new Queue<int>();

                this.BreadthFirstSearchVisit(queue, startIndex, action);
                while (queue.Count > 0)
                {
                    var adjoinVertexIndexs = this.GetNextUnVisitedAdjoinVertexIndexs(queue.Dequeue());
                    foreach (var adjoinVertexIndex in adjoinVertexIndexs)
                    {
                        this.BreadthFirstSearchVisit(queue, adjoinVertexIndex, action);
                    }
                }

                this.ResetVisited();
            }

            private void BreadthFirstSearchVisit(Queue<int> queue, int index, Action<T> action)
            {
                _vertexs[index].WasVisited = true;
                action(_vertexs[index].Value);
                queue.Enqueue(index);
            }

            #endregion

            #region 最小生成树

            public void DisplayMinimumSpanningTree(T startItem)
            {
                var startIndex = this.GetVertexIndex(startItem);
                var queue = new Queue<int>();

                _vertexs[startIndex].WasVisited = true;
                queue.Enqueue(startIndex);
                while (queue.Count > 0)
                {
                    var currentIndex = queue.Dequeue();
                    var adjoinVertexIndexs = this.GetNextUnVisitedAdjoinVertexIndexs(currentIndex);
                    foreach (var adjoinVertexIndex in adjoinVertexIndexs)
                    {
                        Console.WriteLine(_vertexs[currentIndex].Value + "->" + _vertexs[adjoinVertexIndex].Value);

                        _vertexs[adjoinVertexIndex].WasVisited = true;
                        queue.Enqueue(adjoinVertexIndex);
                    }
                }

                this.ResetVisited();
            }

            #endregion

            #region 拓扑排序

            public List<T> TopologySort()
            {
                var cloneVertexs = (Vertex<T>[])_vertexs.Clone();
                var cloneEdges = (bool[][])_edges.Clone();
                var cloneVertexCount = _vertexCount;

                var results = new List<T>();
                while (_vertexCount > 0)
                {
                    var successor = this.NextSuccessor();
                    if (successor == -1)
                    {
                        throw new InvalidOperationException("出现循环了！");
                    }

                    results.Insert(0, _vertexs[successor].Value);

                    this.RemoveVertex(successor);
                    _vertexCount--;
                }

                _vertexs = cloneVertexs;
                _edges = cloneEdges;
                _vertexCount = cloneVertexCount;

                return results;
            }

            private int NextSuccessor()
            {
                for (var row = 0; row < _vertexCount; row++)
                {
                    if (_edges[row].Take(_vertexCount).All(x => !x))
                    {
                        return row;
                    }
                }

                return -1;
            }

            private void RemoveVertex(int successor)
            {
                for (int i = successor; i < _vertexCount - 1; i++)
                {
                    _vertexs[i] = _vertexs[i + 1];
                }

                for (int row = successor; row < _vertexCount - 1; row++)
                {
                    for (int column = 0; column < _vertexCount; column++)
                    {
                        _edges[row][column] = _edges[row + 1][column];
                    }
                }

                for (int column = successor; column < _vertexCount - 1; column++)
                {
                    for (int row = 0; row < _vertexCount; row++)
                    {
                        _edges[row][column] = _edges[row][column + 1];
                    }
                }
            }

            #endregion

            #region 帮助方法

            private int GetVertexIndex(T item)
            {
                for (var i = 0; i < _vertexCount; i++)
                {
                    if (_vertexs[i].Value.Equals(item))
                    {
                        return i;
                    }
                }
                return -1;
            }

            private int GetNextUnVisitedAdjoinVertexIndex(int index)
            {
                for (var i = 0; i < _vertexCount; i++)
                {
                    if (_edges[index][i] && _vertexs[i].WasVisited == false)
                    {
                        return i;
                    }
                }

                return -1;
            }

            private List<int> GetNextUnVisitedAdjoinVertexIndexs(int index)
            {
                var results = new List<int>();

                for (var i = 0; i < _vertexCount; i++)
                {
                    if (_edges[index][i] && _vertexs[i].WasVisited == false)
                    {
                        results.Add(i);
                    }
                }

                return results;
            }

            private void ResetVisited()
            {
                for (var i = 0; i < _vertexCount; i++)
                {
                    _vertexs[i].WasVisited = false;
                }
            }

            #endregion
        }
    }
}
